if (!dojo._hasResource["dojo.dnd.autoscroll"]) { //_hasResource checks added by build. Do not use _hasResource directly in your code.
    dojo._hasResource["dojo.dnd.autoscroll"] = true;
    dojo.provide("dojo.dnd.autoscroll");

    dojo.dnd.getViewport = function() {
        // summary: returns a viewport size (visible part of the window)

        // FIXME: need more docs!!
        var d = dojo.doc, dd = d.documentElement, w = window, b = dojo.body();
        if (dojo.isMozilla) {
            return {w: dd.clientWidth, h: w.innerHeight};	// Object
        } else if (!dojo.isOpera && w.innerWidth) {
            return {w: w.innerWidth, h: w.innerHeight};		// Object
        } else if (!dojo.isOpera && dd && dd.clientWidth) {
            return {w: dd.clientWidth, h: dd.clientHeight};	// Object
        } else if (b.clientWidth) {
            return {w: b.clientWidth, h: b.clientHeight};	// Object
        }
        return null;	// Object
    };

    dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
    dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;

    dojo.dnd.V_AUTOSCROLL_VALUE = 16;
    dojo.dnd.H_AUTOSCROLL_VALUE = 16;

    dojo.dnd.autoScroll = function(e) {
        // summary:
        //		a handler for onmousemove event, which scrolls the window, if
        //		necesary
        // e: Event:
        //		onmousemove event

        // FIXME: needs more docs!
        var v = dojo.dnd.getViewport(), dx = 0, dy = 0;
        if (e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL) {
            dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
        } else if (e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL) {
            dx = dojo.dnd.H_AUTOSCROLL_VALUE;
        }
        if (e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL) {
            dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
        } else if (e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL) {
            dy = dojo.dnd.V_AUTOSCROLL_VALUE;
        }
        window.scrollBy(dx, dy);
    };

    dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
    dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};

    dojo.dnd.autoScrollNodes = function(e) {
        // summary:
        //		a handler for onmousemove event, which scrolls the first avaialble
        //		Dom element, it falls back to dojo.dnd.autoScroll()
        // e: Event:
        //		onmousemove event

        // FIXME: needs more docs!
        for (var n = e.target; n;) {
            if (n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)) {
                var s = dojo.getComputedStyle(n);
                if (s.overflow.toLowerCase() in dojo.dnd._validOverflow) {
                    var b = dojo._getContentBox(n, s), t = dojo._abs(n, true);
                    // console.debug(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
                    b.l += t.x + n.scrollLeft;
                    b.t += t.y + n.scrollTop;
                    var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2),
                            h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
                            rx = e.pageX - b.l, ry = e.pageY - b.t, dx = 0, dy = 0;
                    if (rx > 0 && rx < b.w) {
                        if (rx < w) {
                            dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
                        } else if (rx > b.w - w) {
                            dx = dojo.dnd.H_AUTOSCROLL_VALUE;
                        }
                    }
                    //console.debug("ry =", ry, "b.h =", b.h, "h =", h);
                    if (ry > 0 && ry < b.h) {
                        if (ry < h) {
                            dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
                        } else if (ry > b.h - h) {
                            dy = dojo.dnd.V_AUTOSCROLL_VALUE;
                        }
                    }
                    var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
                    n.scrollLeft = n.scrollLeft + dx;
                    n.scrollTop = n.scrollTop + dy;
                    // if(dx || dy){ console.debug(oldLeft + ", " + oldTop + "\n" + dx + ", " + dy + "\n" + n.scrollLeft + ", " + n.scrollTop); }
                    if (oldLeft != n.scrollLeft || oldTop != n.scrollTop) {
                        return;
                    }
                }
            }
            try {
                n = n.parentNode;
            } catch(x) {
                n = null;
            }
        }
        dojo.dnd.autoScroll(e);
    };

}
